// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

// Tests for the BufferTracker.

#include "gpu/command_buffer/client/buffer_tracker.h"

#include <GLES2/gl2.h>
#include <GLES2/gl2ext.h>
#include <stddef.h>
#include <stdint.h>

#include <memory>

#include "gpu/command_buffer/client/client_test_helper.h"
#include "gpu/command_buffer/client/gles2_cmd_helper.h"
#include "gpu/command_buffer/client/mapped_memory.h"
#include "gpu/command_buffer/common/command_buffer.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"

namespace gpu {
namespace gles2 {

    class MockClientCommandBufferImpl : public MockClientCommandBuffer {
    public:
        MockClientCommandBufferImpl()
            : MockClientCommandBuffer()
            , context_lost_(false)
        {
        }
        ~MockClientCommandBufferImpl() override { }

        scoped_refptr<gpu::Buffer> CreateTransferBuffer(size_t size,
            int32_t* id) override
        {
            if (context_lost_) {
                *id = -1;
                return NULL;
            }
            return MockClientCommandBuffer::CreateTransferBuffer(size, id);
        }

        void set_context_lost(bool context_lost)
        {
            context_lost_ = context_lost;
        }

    private:
        bool context_lost_;
    };

    class BufferTrackerTest : public testing::Test {
    protected:
        static const int32_t kNumCommandEntries = 400;
        static const int32_t kCommandBufferSizeBytes = kNumCommandEntries * sizeof(CommandBufferEntry);

        void SetUp() override
        {
            command_buffer_.reset(new MockClientCommandBufferImpl());
            helper_.reset(new GLES2CmdHelper(command_buffer_.get()));
            helper_->Initialize(kCommandBufferSizeBytes);
            mapped_memory_.reset(
                new MappedMemoryManager(helper_.get(), MappedMemoryManager::kNoLimit));
            buffer_tracker_.reset(new BufferTracker(mapped_memory_.get()));
        }

        void TearDown() override
        {
            buffer_tracker_.reset();
            mapped_memory_.reset();
            helper_.reset();
            command_buffer_.reset();
        }

        std::unique_ptr<MockClientCommandBufferImpl> command_buffer_;
        std::unique_ptr<GLES2CmdHelper> helper_;
        std::unique_ptr<MappedMemoryManager> mapped_memory_;
        std::unique_ptr<BufferTracker> buffer_tracker_;
    };

    TEST_F(BufferTrackerTest, Basic)
    {
        const GLuint kId1 = 123;
        const GLuint kId2 = 124;
        const GLsizeiptr size = 64;

        // Check we can create a Buffer.
        BufferTracker::Buffer* buffer = buffer_tracker_->CreateBuffer(kId1, size);
        ASSERT_TRUE(buffer != NULL);
        // Check we can get the same Buffer.
        EXPECT_EQ(buffer, buffer_tracker_->GetBuffer(kId1));
        // Check mapped memory address.
        EXPECT_TRUE(buffer->address() != NULL);
        // Check shared memory was allocated.
        EXPECT_EQ(1lu, mapped_memory_->num_chunks());
        // Check we get nothing for a non-existent buffer.
        EXPECT_TRUE(buffer_tracker_->GetBuffer(kId2) == NULL);
        // Check we can delete the buffer.
        buffer_tracker_->RemoveBuffer(kId1);
        // Check shared memory was freed.
        mapped_memory_->FreeUnused();
        EXPECT_EQ(0lu, mapped_memory_->num_chunks());
        // Check we get nothing for a non-existent buffer.
        EXPECT_TRUE(buffer_tracker_->GetBuffer(kId1) == NULL);
    }

    TEST_F(BufferTrackerTest, ZeroSize)
    {
        const GLuint kId = 123;

        // Check we can create a Buffer with zero size.
        BufferTracker::Buffer* buffer = buffer_tracker_->CreateBuffer(kId, 0);
        ASSERT_TRUE(buffer != NULL);
        // Check mapped memory address.
        EXPECT_TRUE(buffer->address() == NULL);
        // Check no shared memory was allocated.
        EXPECT_EQ(0lu, mapped_memory_->num_chunks());
        // Check we can delete the buffer.
        buffer_tracker_->RemoveBuffer(kId);
    }

    TEST_F(BufferTrackerTest, LostContext)
    {
        const GLuint kId = 123;
        const GLsizeiptr size = 64;

        command_buffer_->set_context_lost(true);
        // Check we can create a Buffer when after losing context.
        BufferTracker::Buffer* buffer = buffer_tracker_->CreateBuffer(kId, size);
        ASSERT_TRUE(buffer != NULL);
        // Check mapped memory address.
        EXPECT_EQ(64u, buffer->size());
        // Check mapped memory address.
        EXPECT_TRUE(buffer->address() == NULL);
        // Check no shared memory was allocated.
        EXPECT_EQ(0lu, mapped_memory_->num_chunks());
        // Check we can delete the buffer.
        buffer_tracker_->RemoveBuffer(kId);
    }

    TEST_F(BufferTrackerTest, Unmanage)
    {
        const GLuint kId = 123;
        const GLsizeiptr size = 64;

        BufferTracker::Buffer* buffer = buffer_tracker_->CreateBuffer(kId, size);
        ASSERT_TRUE(buffer != NULL);
        EXPECT_EQ(mapped_memory_->bytes_in_use(), static_cast<size_t>(size));

        void* mem = buffer->address();
        buffer_tracker_->Unmanage(buffer);
        buffer_tracker_->RemoveBuffer(kId);
        EXPECT_EQ(mapped_memory_->bytes_in_use(), static_cast<size_t>(size));

        mapped_memory_->Free(mem);
        EXPECT_EQ(mapped_memory_->bytes_in_use(), static_cast<size_t>(0));
    }

} // namespace gles2
} // namespace gpu
